/* ----------------------------------------------------------------------------------------------------------------------
 * Monitor
 * -----------------------------------------------------------------------------------------------------------------------
 */

/* 
   优化方案.<预> : 
	 
		1. 单Monitor情况下，使用单例Monitor.
		
		2. 判断世界上全部实体是否在Monitor范围内，从World移入，移除，或毁灭.
 */ 
package pure.engine.monitor 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.ColorTransform;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import pure.engine.entity.Fragment;
	import pure.engine.system.SystemManager;
	import pure.engine.world.WorldBody;
	import pure.engine.world.WorldContent;
	
	import pure.engine.entity.Entity;
	import pure.engine.entity.EntityManager;
	import pure.engine.entity.EntityPool;
	import pure.engine.entity.Mirror;
	import pure.engine.events.WorldContentEvent;
	import pure.engine.system.IFrameObject;
	import pure.engine.system.ProcessManager;
	import pure.engine.Multiple;
	import pure.engine.system.ns_despair;
	
	use namespace ns_despair;
	
	// 监视器抽象类
	// [author] - Pure
	
	// [param] - <width>      宽度
	// [param] - <height>     高度
	// [param] - <worldBody>  被观察的世界
	// [param] - <duration>   渐变持续时间
	// [param] - <color>      渐变颜色
	
	// [spawn] - <Despair - ＃createMonitor>
public class MonitorCore extends Sprite implements IFrameObject
{
	public function MonitorCore(width:Number, height:Number, worldBody:WorldBody, duration:Number, color:uint)
	{
		m_width             =  width;
		m_height            =  height;
		m_worldBody         =  worldBody;
		_autoNofityWorld    =  true;
		super.mouseEnabled  =  this.mouseChildren  =  false;
		m_duration          =  cachedCount         =  duration * 1000.0;
		m_color             =  color;

		this.addEventListener(Event.ADDED_TO_STAGE, ____onAddedToStage);
	}
	
	
	
	// ----------------------------------［ Const ］----------------------------------

	public static const LEFT_TOP:String      =  'LT',
						RIGHT_TOP:String     =  'RT',
						LEFT_BOTTOM:String   =  'LB',
						RIGHT_BOTTOM:String  =  'RB';

	
						
	
	//======================
	// Member
	//======================

	
	protected var m_worldBody:WorldBody;
	
	protected var m_worldContent:WorldContent;
		
	protected var m_x:Number = 0, m_y:Number = 0, m_width:Number, m_height:Number;
	
	protected var _sceneryBmpList:Array = [];
	
	protected var _mirrorContainer:Sprite = new Sprite();
	
	protected var _mask:Shape;

	protected var m_boundsW:int, m_boundsH:int;
	
	protected var _align:String, _followed:String;
	
	protected var _waiting:Boolean// = true;
	
	
	
	
	
	//======================
	// Cached Object
	//======================

	protected var _entityOldX:Number, _entityOldY:Number, m_duration:Number;
	
	protected var m_color:uint;

	protected var _worldDirty:Boolean, _alphaDirty:Boolean, _fading:Boolean, _scopeDirty:Boolean, _exiting:Boolean, _entering:Boolean;
	
	protected var _autoNofityWorld:Boolean;    // 自动通知世界视域更新
	
	protected var cachedColorBmd:BitmapData;  // 色彩画像(过渡用)
	
	protected var cachedColorBmp:Bitmap;  // 色彩容器(过渡用)
	
	protected var cachedImageBmd:BitmapData;  // 画像缓存(过渡用)

	protected var cachedImageBmp:Bitmap;  // 画像容器(过渡用)
	
	protected var cachedCount:int;

	protected static var cachedPoint:Point = new Point();
	
	protected static var cachedMirrors:Array = [];

	
	
	
	// 毁灭
	public function destroy():void
	{
		ProcessManager.instance.removeFrameObject(this);
		Multiple.m_notifier.removeEventListener(WorldContentEvent.WORLD_CHANGE,  ____onWorldChange);
		Multiple.m_notifier.removeEventListener(WorldContentEvent.WORLD_WAITING, ____onWorldWaiting);
		
		if ( !_waiting ) 
		{
			_clearAllBuffers(true);
		}
		
		if ( cachedImageBmd )
		{
			cachedImageBmd.dispose();
			cachedImageBmd  =  null;
			cachedImageBmp  =  null;
		}
		
		cachedColorBmd.dispose();
		cachedColorBmd     =  null;
		cachedColorBmp     =  null;
		this.mouseEnabled  =  false;
		m_worldBody         =  null;
		m_worldContent         =  null;
		_sceneryBmpList    =  null;
		_mirrorContainer   =  null;
		_mask              =  null;
		this.mask          =  null;
	}

	
	/**
	 * 移动至世界坐标
	 * 
	 * @param wx
	 * @param wy
	 */
	public function moveInWorld(wx:Number, wy:Number):void
	{
		if (!m_worldContent||(m_x == wx && m_y == wy))  return;
		
		m_x          =  wx;
		m_y          =  wy;
		_scopeDirty  =  true;
		
		_clampInWorld();
	}
	
	
	/**
	 * 滚动指定坐标
	 * 
	 * @param x
	 * @param y
	 */
	//public function scrollInWorld(x:Number, y:Number):void 
	//{
		//if (x == 0 || y == 0)  return;
		//
		//m_x         +=  x;
		//m_y         +=  y;
		//_scopeDirty  =  true;
		//
		//_clampInWorld();
	//}
	

	/**
	 * 追踪目标（不断通过实体管理器查找目标）
	 * 
	 * @param entityName
	 * @param boundsWidth
	 * @param boundsHeight
	 */
	public function follow(entity:String, boundsWidth:int = 0, boundsHeight:int = 0):void
	{
		if (boundsWidth  < 0)              throw new Error('Monitor - ＃follow - 参数错误，bounds宽度不可为负数.');
		if (boundsHeight < 0)              throw new Error('Monitor - ＃follow - 参数错误，bounds高度不可为负数.');
		if (boundsWidth  > m_width)   throw new Error('Monitor - ＃follow - 参数错误，bounds宽度超出监视器范围.');
		if (boundsHeight > m_height)  throw new Error('Monitor - ＃follow - 参数错误，bounds高度超出监视器范围.');
		
		_followed      =  entity;
		m_boundsW   =  boundsWidth;
		m_boundsH  =  boundsHeight;
	}

	
	public function onFrame(elapsedT:Number):void
	{
		var color:uint;
		var alpha:Number;
			
		// 淡出
		if ( _exiting )
		{		
			cachedCount      +=  elapsedT;
			if (cachedCount  >=  m_duration) 
			{
				this.removeChild(cachedImageBmp);
				cachedImageBmd.dispose();
				cachedImageBmd   =  null;
				cachedImageBmp   =  null;
				cachedCount   =  m_duration;
				_exiting           =  false;
				_waiting           =  true;
				alpha              =  0xFF;
			}
			else
			{
				alpha              =  int((cachedCount / m_duration) * 256)
			}
			color  =  m_color | (alpha << 24);
			cachedColorBmd.fillRect(cachedColorBmd.rect, color);
			return;
		}

		// 新世界
		if ( _worldDirty )
		{
			_worldDirty = false;
			if (m_worldBody.m_world)
			{
				// 更新世界
				_advanceWorld();
				
				// 更新残像(FORCE)
				_advanceMirror(true);

				
				_waiting   =  false;
				_entering  =  true;
			}
			else
			{
				return;
			}
		}

		// 等待
		if ( _waiting )  return;

		// 淡入
		if ( _entering )
		{
			cachedCount      -=  elapsedT;
			if ( cachedCount <=  0 ) 
			{
				this.removeChild(cachedColorBmp);
				cachedCount  =  0;
				_entering    =  false; 
			}
			else
			{
				alpha  =  int((cachedCount / m_duration) * 256)
				color  =  m_color | (alpha << 24);
				cachedColorBmd.fillRect( cachedColorBmd.rect, color );
				this.setChildIndex(cachedColorBmp, this.numChildren - 1);
			}
		}
		
		// 视野
		if (_scopeDirty)
		{
			m_worldContent.updateSceneriesByArea(m_x, m_y, m_width, m_height, _sceneryBmpList);
			if(_autoNofityWorld)
			{
				_nofityScopeUpdate();
			}
			_scopeDirty = false;
		}
		
		// 更新追踪(视域)
		if (_followed != null && _followed != '')
		{
			_advanceFollow();
		}

		// 更新残像
		_advanceMirror(false);

	}
	

	
	/* ----------------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★  get × set  ★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	

	
	/**
	 * 对齐
	 */
	public function set align(value:String):void
	{
		_align = value;
		if (!_waiting)
		{
			_doAlign();
		}
	}

	

	
	/* ----------------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★  private  ★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------------
	 */
	


	 
	/**
	 * 对齐
	 */
	protected function _doAlign():void 
	{
		switch( _align ) 
		{
			case LEFT_TOP:
				m_x  =  0;
				m_y  =  0;
				break;
				
			case LEFT_BOTTOM:
				m_x  =  0;
				m_y  =  m_worldContent.m_height -  m_height;
				break;
				
			case RIGHT_TOP:
				m_x  =  m_worldContent.m_width  -  m_width;
				m_y  =  0;
				break;
				
			case RIGHT_BOTTOM:
				m_x  =  m_worldContent.m_width  -  m_width;
				m_y  =  m_worldContent.m_height -  m_height;
				break;
				
			default:
				break;
		}
	}
	
	
	/**
	 * 执行更新世界
	 */
	protected function _advanceWorld():void
	{
		m_worldContent = m_worldBody.m_world;
		
		if ( m_worldContent.m_width < m_width || m_worldContent.m_height < m_height )
		{
			throw new Error("Monitor - ＃ ____onWorldChange - 监视范围超出世界.");
		}
		
		_makeAllBuffers();
		_doAlign();
	}
	
	
	/**
	 * 生成全部缓存
	 */
	protected function _makeAllBuffers():void 
	{
		var buffer:BitmapData;
		var bmp:Bitmap;
		var list:Array;
		var len:int;
		
		list  =  m_worldContent.sceneryBuffers;
		len   =  list.length;
		
		for (var i:int = 0; i < len; i++)
		{
			buffer  =  list[i]
			bmp     =  new Bitmap(buffer.clone());
			this.addChild(bmp);
			_sceneryBmpList.push(bmp);
		}
		this.addChild(_mirrorContainer);
	}
	

	/**
	 * 清除全部缓存
	 * 
	 * @param entire
	 */
	protected function _clearAllBuffers(entire:Boolean):void
	{
		var bmp:Bitmap;
		var M:Mirror;
		var len:int, i:int;
		
		while (this.numChildren)  removeChildAt(0);
		
		while (_mirrorContainer.numChildren)
		{
			M = _mirrorContainer.removeChildAt(0) as Mirror;
			M.recycle();
		}

		if (!entire)
		{
			this.addChild(_mask);
		}
		
		len = _sceneryBmpList.length;
		for (i = 0; i < len; i++)
		{
			bmp = _sceneryBmpList[i];
			//bmp.bitmapData.dispose();
			bmp.bitmapData = null;
		}
		_sceneryBmpList.length = 0;
	}

	
	/**
	 * 执行更新镜头追踪（实体）
	 */
	private function _advanceFollow():void
	{
		var list:Array;
		var E:Entity
		var flagH:Boolean, flagV:Boolean;

		list = EntityManager.m_entityGroups[_followed];
		if (list)
		{
			E      =  list[0];
			flagH  =  _rejustHorizontal(E);
			flagV  =  _rejustVertical(E);
			
			if (flagH || flagV)
			{
				_clampInWorld();
				_scopeDirty = true;
			}
		}
	}
	
	
	/**
	 * 执行更新残像
	 * 
	 * @param force
	 */
	private function _advanceMirror(force:Boolean):void
	{
		var lenC:int, i:int, lenA:int;
		var M:Mirror;
		var E:Entity;
		var list:Array;
		
		list  =  m_worldBody.m_visibleList;
		lenA  =  m_worldBody.m_numVisible;
		lenC  =  _mirrorContainer.numChildren;
		
		if (!force)
		{
			for (i = lenC - 1; i >= 0; i--)
			{
				M = _mirrorContainer.getChildAt(i) as Mirror;
				if (!M.entity.m_visible)
				{
					_mirrorContainer.removeChild(M);
					M.entity.mirrorExisted = false;
					M.recycle();
				}
				else
				{
					E    =  M.entity;
					M.x  =  E.m_x - m_x;
					M.y  =  E.m_y - m_y;
					
					M.update();
					cachedMirrors.push(M);
					M.entity.mirrorExisted = true;
				}
			}

			for (i = 0; i < lenA; i++)
			{
				E = list[i];
				if (!E.mirrorExisted)
				{
					M    =  EntityPool.makeMirror();
					M.x  =  E.m_x - m_x;
					M.y  =  E.m_y - m_y;
					
					M.binding(E);	
					cachedMirrors.push(M);
					_mirrorContainer.addChild(M);
				}
			}
		}
		
		else
		{
			for (i = lenC - 1; i >= 0; i--)
			{
				M = _mirrorContainer.removeChildAt(i) as Mirror;
				M.entity.mirrorExisted = false;
				M.recycle();
			}
			
			for (i = 0; i < lenA; i++)
			{
				E    =  list[i];
				M    =  EntityPool.makeMirror()
				M.x  =  E.m_x - m_x;
				M.y  =  E.m_y - m_y;
				
				M.binding(E);	
				cachedMirrors.push(M);
				_mirrorContainer.addChild(M);
			}
		}
		
		cachedMirrors.sortOn(m_worldBody.m_sortData, Array.NUMERIC);
		lenC = cachedMirrors.length;
		for (i = 0; i < lenC; i++)
		{
			M = cachedMirrors[i];
			if (_mirrorContainer.getChildIndex(M) != i)
			{
				_mirrorContainer.setChildIndex(M, i);
			}
		}
		cachedMirrors.length = 0;
	}
	

	// 保持视野在世界范围
	private function _clampInWorld():void 
	{
		if (m_x < 0)                         m_x  =  0;
		if (m_y < 0)                         m_y  =  0;
		if (m_x+m_width  > m_worldContent.m_width)   m_x  =  m_worldContent.m_width  - m_width;
		if (m_y+m_height > m_worldContent.m_height)  m_y  =  m_worldContent.m_height - m_height;
	}
	
	
	/**
	 * 水平调整位置
	 */
	private function _rejustHorizontal(E:Entity):Boolean
	{
		var trackedX:Number;
		
		if (E.m_x == _entityOldX)  return false;
		
		if (m_boundsW == 0) 
		{
			m_x = E.m_x - (m_width >> 1);
		}
		else
		{
			trackedX = E.m_x - m_x;
			if (trackedX < (m_width - m_boundsW) >> 1 ) 
			{
				trackedX  =  (m_width - m_boundsW) >> 1;
				m_x  =  E.m_x - trackedX;
			}
			else if (trackedX > (m_width + m_boundsW) >> 1 )
			{
				trackedX  =  (m_width + m_boundsW) >> 1;
				m_x  =  E.m_x - trackedX;
			}
		}
		_entityOldX = E.m_x;
		return true;
	}
	
	
	/**
	 * 垂直调整位置
	 */
	private function _rejustVertical(E:Entity):Boolean 
	{
		var trackedY:Number;
		
		if (E.m_y == _entityOldY)  return false;
		
		if (m_boundsH == 0) 
		{
			m_y = E.m_y - (m_height >> 1);
		}
		else
		{
			trackedY = E.m_y - m_y;
			if (trackedY < (m_height - m_boundsH) >> 1 ) 
			{
				trackedY = (m_height - m_boundsH) >> 1;
				m_y = E.m_y - trackedY;
			}
			else if (trackedY > (m_height + m_boundsH) >> 1 )
			{
				trackedY = (m_height + m_boundsH) >> 1;
				m_y = E.m_y - trackedY;
			}
		}
		_entityOldY = E.m_y;
		return true
	}

	
	
	
	
	
	//======================
	// Event
	//======================
	
	
	// [Event]
	// 初期化
	protected function ____onAddedToStage(e:Event):void
	{
		this.removeEventListener( Event.ADDED_TO_STAGE, ____onAddedToStage );

		Multiple.m_notifier.addEventListener(WorldContentEvent.WORLD_CHANGE,  ____onWorldChange);
		Multiple.m_notifier.addEventListener(WorldContentEvent.WORLD_WAITING, ____onWorldWaiting);
		ProcessManager.instance.addFrameObject(this, -10);
		
		if (m_worldBody.m_world)
		{
			_worldDirty = true;
		}
		else
		{
			_waiting = true;
		}
		
		// 遮罩
		_mask             =  new Shape();
		this.mask         =  _mask;
		this.addChild(_mask);
		_mask.graphics.beginFill(0, 0);
		_mask.graphics.drawRect(0, 0, m_width, m_height);

		// 过渡
		cachedColorBmd    =  new BitmapData(m_width, m_height, true, 0x0);
		cachedColorBmp    =  new Bitmap(cachedColorBmd);
		cachedColorBmd.fillRect(cachedColorBmd.rect, m_color | (0xFF << 24));
		this.addChild(cachedColorBmp);
	}
	 
	
	// [Event]
	// 等待中
	protected function ____onWorldWaiting(e:WorldContentEvent):void 
	{
		if (_exiting || m_worldBody.m_name != e.worldBodyName)  return;
		
		if (_entering)
		{
			_entering    =  false;
			_worldDirty  =  true;
		}
		
		if (!cachedImageBmd)
		{
			cachedImageBmd  =  new BitmapData( m_width, m_height, true, 0x0 );
			cachedImageBmp  =  new Bitmap( cachedImageBmd );
			cachedImageBmd.draw(this);
		}

		_clearAllBuffers(false);
		this.addChild(cachedImageBmp);
		this.addChild(cachedColorBmp);

		cachedCount  =  0;
		_exiting          =  true;
		_waiting          =  true;
		m_worldContent    =  null;
	}
	
	
	
	// [Event]
	// 新世界
	protected function ____onWorldChange(e:WorldContentEvent):void
	{	
		if (m_worldBody.m_name != e.worldBodyName)  return;

		_worldDirty  =  true;
		_scopeDirty  =  true;
	}

	
	/**
	 * 通知更新视域
	 */
	protected function _nofityScopeUpdate():void
	{
		m_worldContent.m_top     =  m_y >> 0;
		m_worldContent.m_bottom  =  m_y+m_height >> 0;
		m_worldContent.m_left    =  m_x >> 0;
		m_worldContent.m_right   =  m_x+m_width >> 0;
		
		//trace('\n[Scope]      ' + (m_worldContent.m_top >> 0)                                                               + '\n'
		                        //+ '        '     + (m_worldContent.m_left >> 0) + '       ' + (m_worldContent.m_right >> 0) + '\n'
							    //+ '            ' + (m_worldContent.m_bottom >> 0));
	}


}

}